\documentclass{article}

\usepackage[utf8]{inputenc}
\usepackage{lmodern}
\usepackage[T1]{fontenc}
\usepackage[french]{babel}
\usepackage{graphicx}
\usepackage{fancyvrb}
\usepackage{xcolor}

\setlength{\textwidth}{16cm}
\setlength{\evensidemargin}{0.5cm}
\setlength{\oddsidemargin}{0.5cm}

\newsavebox{\BBbox}
\newenvironment{DDbox}[1]{
\begin{lrbox}{\BBbox}\begin{minipage}{\linewidth}}
{\end{minipage}\end{lrbox}\noindent\colorbox{Zgris}{\usebox{\BBbox}} \\
[.5cm]}

\definecolor{Zgris}{rgb}{0.93,0.93,0.93}

\title{Projet SGBD: Bandes dessinées}

\begin{document}
\date{14 Décembre 2012}
\author{Thibaud Lambert, Clément Pinet, Valentin Sarthou}

\maketitle

\newpage

\tableofcontents

\newpage
\section*{Introduction}

Le projet consiste en la réalisation d’une base de données permettant de gérer des bandes dessinées. Il faudra donc créer la base, la remplir avec un jeu de données représentatif et écrire plusieurs requêtes permettant de récupérer certaines informations dans la base.

\section{Modèle}

Dans un premier temps, le modèle entité-association sera présenté. Ensuite, le passage vers le modèle relationnel sera expliqué. Et enfin, nous vérifierons qu’elle est en 3ème forme normale.

\subsection{Modèle entité-association}

Nous avons identifié 8 entités différentes : Volume, Album, Collection, Revue, Histoire, Auteur, Série, Éditeur. Les entités Album et Revue héritent de l’entité Volume.
On peut remarquer qu’avec le modèle entité-association proposé, deux album appartenant à la même collection peuvent avoir un éditeur différent. Pour résoudre ce problème, nous avons fait le choix de mettre en place un trigger qui permettra de vérifier lors de l’ajout d’un nouvel Album qu’il a le même éditeur que les autres Album de sa collection.

\begin{center}
\includegraphics[scale=0.5]{entite-association.png}
\end{center}

L'interface développée pour ce projet proposera la consultation, l'ajout, la suppression et la mise à jour de chacune des entités et relations.

\subsection{Modèle relationnel}

Le passage du modèle entité-association au modèle relationnel se fait sans difficulté particulière. Les associations Apparaître, Dessine, et Scénarise deviennent des relations du fait de leurs cardinalités.

\begin{center}
\includegraphics[scale=0.5]{relationnel.png}
\end{center}

Le \$ signifie que cet attribut est la clé primaire de cette relation. Quand au \# cela signifie que c’est une clé étrangère.

~

\noindent Voila un résumé des différentes relations obtenues:

\noindent VOLUME (\underline{NUMERO\_VOLUME} , ANNEE\_EDITION , TITRE\_VOLUME, \#NUMERO\_EDITEUR)\\
\noindent ALBUM (\underline{\#NUMERO\_VOLUME} , \#NUMERO\_COLLECTION , NUMERO\_DANS\_COLLECTION)\\
\noindent COLLECTION (\underline{NUMERO\_COLLECTION} , NOM\_COLLECTION)\\
\noindent REVUE (\underline{\#NUMERO\_VOLUME} , NOM\_REVUE)\\
\noindent HISTOIRE (\underline{NUMERO\_HISTOIRE} , ANNEE\_PARUTION , \#NUMERO\_SERIE, NUMERO\_DANS\_SERIE)\\
\noindent SERIE (\underline{NUMERO\_SERIE} , NOM\_SERIE)\\
\noindent APPARAITRE (\underline{\#NUMERO\_HISTOIRE , \#NUMERO\_VOLUME} , ANNOTATION)\\
\noindent DESSINE (\underline{\#NUMERO\_AUTEUR, \#NUMERO\_HISTOIRE})\\
\noindent SCENARISE (\underline{\#NUMERO\_AUTEUR, \#NUMERO\_HISTOIRE})\\
\noindent AUTEUR (\underline{NUMERO\_AUTEUR}, NOM\_AUTEUR)\\

\subsection{Normalisation}

\subsubsection{1ère Forme normale}

Toutes les relations admettent une clé et chaque attribut contient une valeur atomique.

\subsubsection{2ème Forme normale}

Tout attribut non clé d’une relation ne dépend pas d’une partie de la clé de celle-ci . Les seules relations avec des clés composées sont APPARAITRE, DESSINE et SCENARISE. DESSINE et SCENARISE n’ont pas d’attribut non clé. Pour APPARAITRE, l’ANNOTATION  est spécifique à la fois à un NUMERO\_HISTOIRE et un NUMERO\_VOLUME.

\subsubsection{3ème Forme normale}

Tout attribut non clé d’une relation ne dépend pas d’un autre attribut non clé. Les seules relations avec plusieurs attributs non clé sont VOLUME, ALBUM, et HISTOIRE. Or ces attributs n’ont pas de lien entre eux: par exemple ANNEE\_EDITION ne dépend pas de TITRE\_VOLUME.

\subsection{Contraintes d’intégrité et dépendances fonctionnelles}

Lors de l’ajout d’une nouvelle entrée dans la table DESSINATEUR ou SCENARISTE il faut d’abord vérifier que le NUMERO\_HISTOIRE et le NUMERO\_AUTEUR correspondent bien à une entrée dans les tables respectives HISTOIRE et AUTEUR. De même avant de rajouter une nouvelle entrée dans REVUE ou ALBUM, il faut ajouter l’entrée correspondante dans la table VOLUME. Pour ajouter un nouveau volume, on doit d’abord ajouter l’éditeur correspondant si il n’existe pas déjà. Et finalement pour l’ajout d’une nouvelle entrée dans la table APPARAITRE, l’histoire et le volume en question doivent être dans leurs tables respectives.

\section{Implémentation}

Pour réaliser ce projet, nous avons choisi d’utiliser le système MySQL
pour la base de données et le langage PHP pour l'interface.\\ Dans le
dossier \textit{SQL}, le script \textit{base\_mysql.sql} créé les
tables nécessaires au fonctionnement de la base de données. Il ajoute
également les contraintes entre tables en prenant en compte les clés
étrangères présentes dans chaque table.\\ Le script
\textit{donnees\_mysql.sql} remplit les tables créées avec des données
prédéfinies et arbitraires, mais pertinentes vis à vis des requêtes de
consultation demandées.\\ Le script \textit{suppression\_mysql.sql}
vide et supprime les tables créées par le script
\textit{base\_mysql.sql}.\\ Le fichier \textit{requetes\_mysql.sql}
contient toutes les requêtes demandées sauf la derniére (classement des auteurs par décade). La description des requêtes
est indiquée en commentaire au dessus de chacune d'elles.

\subsection{Trigger}
Afin de garantir l'unicité d'un éditeur pour une collection donnée, trois triggers ont été mis en place : un lors de l'ajout d'un album, un autre lors de la mise à jour d'un album et un dernier lors de la mise à jour d'un volume.\\
Les triggers vérifient si l'éditeur de l'entrée en question correspond à l'éditeur des autres albums appartenant à la même collection. Si ce n'est pas le cas, la modification inverse est faite.\\

\begin{DDbox}{\linewidth}
\begin{Verbatim}
delimiter //

create trigger collection_trigger_insert after insert
on ALBUM for each row

begin

set @num_ed = (select distinct NUMERO_EDITEUR
from VOLUME
where NUMERO_VOLUME in
(select ALBUM.NUMERO_VOLUME from ALBUM
 where ALBUM.NUMERO_COLLECTION = new.NUMERO_COLLECTION));

set @num_edd_add = (select VOLUME.NUMERO_EDITEUR
from VOLUME
where VOLUME.NUMERO_VOLUME=new.NUMERO_VOLUME);

if @num_edd <> @num_edd_add then
delete from ALBUM where ALBUM.NUMERO_VOLUME=new.NUMERO_VOLUME;
end if;

end //
delimiter ;
\end{Verbatim}
\end{DDbox}
Après avoir ajouté l'entrée, on effectue une requête pour obtenir le numéro d'éditeur de l'album ajouté et une autre pour obtenir le numéro d'éditeur des albums de la collection. On compare ensuite ces deux valeurs et on effectue la suppression de l'entrée ajoutée si elles sont différentes.\\

Remarque: L'activation des triggers n'est possible qu'en executant le fichier \textit{base\_mysql.sql} dans mysql et non dans l'interface. 

\begin{DDbox}{\linewidth}
\begin{Verbatim}
delimiter //

create trigger collection_trigger_update_album after update
on ALBUM for each row

begin

set @num_ed = (select distinct NUMERO_EDITEUR
from VOLUME
where NUMERO_VOLUME in
(select ALBUM.NUMERO_VOLUME from ALBUM
 where ALBUM.NUMERO_COLLECTION = new.NUMERO_COLLECTION));

set @num_edd_add = (select VOLUME.NUMERO_EDITEUR
from VOLUME
where VOLUME.NUMERO_VOLUME=new.NUMERO_VOLUME);

if @num_edd <> @num_edd_add then
UPDATE ALBUM set ALBUM.NUMERO_COLLECTION=old.NUMERO_COLLECTION, 
                 ALBUM.NUMERO_DANS_COLLECTION=old.NUMERO_DANS_COLLECTION 
where ALBUM.NUMERO_VOLUME=new.NUMERO_VOLUME;
end if;

end //
delimiter ;
\end{Verbatim}
\end{DDbox}
Après avoir modifié l'entrée dans la table ALBUM, comme précédemment on vérifie les numéros d'éditeurs.


\begin{DDbox}{\linewidth}
\begin{Verbatim}
delimiter //

create trigger collection_trigger_update_volume after update
on VOLUME for each row

begin
if new.NUMERO_EDITEUR <> old.NUMERO_EDITEUR then

   set @num = (select count(*) as NUM from ALBUM 
        where ALBUM.NUMERO_COLLECTION in 
    		  (select NUMERO_COLLECTION FROM ALBUM 
		    	  where ALBUM.NUMERO_VOLUME=new.NUMERO_VOLUME));

if @num > 1 then
UPDATE VOLUME set VOLUME.TITRE_VOLUME=old.TITRE_VOLUME, 
                  VOLUME.NUMERO_EDITEUR=old.NUMERO_EDITEUR, 
                  VOLUME.ANNEE_EDITION=old.ANNEE_EDITION 
where VOLUME.NUMERO_VOLUME=new.NUMERO_VOLUME;
end if;
end if;

end //
delimiter ;

\end{Verbatim}
\end{DDbox}

On peut changer l'éditeur d'un volume seulement si ce volume est seul dans sa collection ou si il n'appartient à aucune collection.

\subsection{Mise à jour}

Voici quelques exemples de requêtes permettant de tenir à jour la base de données: une requête d'ajout , une de mise à jour et deux de suppresion.

\begin{DDbox}{\linewidth}
\begin{Verbatim}
-- Ajouter un éditeur
insert into EDITEUR values ( 1, 'HACHETTE');

-- Changer le nom d'un auteur 
UPDATE AUTEUR set NOM_AUTEUR='LY' where NUMERO_AUTEUR=4;

-- Supprimer une histoire
DELETE from HISTOIRE where NUMERO_HISTOIRE=2;

-- Supprimer toutes les histoires d'un auteur
DELETE from HISTOIRE 
where NUMERO_HISTOIRE in (select NUMERO_HISTOIRE from DESSINE 
                          where NUMERO_AUTEUR in (select NUMERO_AUTEUR from AUTEUR 
                                                  where NOM_AUTEUR='HUB'));
\end{Verbatim}
\end{DDbox}



\subsection{Requêtes}

\subsubsection{Infos sur un album}


\begin{DDbox}{\linewidth}
\begin{Verbatim}
select R3.NUMERO_VOLUME, TITRE_VOLUME, NOM_COLLECTION, NUMERO_DANS_COLLECTION from
((select C.NOM_COLLECTION, C.NUMERO_COLLECTION from COLLECTION C) as R1
   right outer join
   ((select A.NUMERO_COLLECTION, A.NUMERO_VOLUME,
            A.NUMERO_DANS_COLLECTION from ALBUM A) as R2
	 inner join
	  (select V.NUMERO_VOLUME, V.TITRE_VOLUME from VOLUME V
	  where V.NUMERO_VOLUME = numero_du_volume) as R3
	 on R3.NUMERO_VOLUME = R2.NUMERO_VOLUME)
   on R2.NUMERO_COLLECTION = R1.NUMERO_COLLECTION);
\end{Verbatim}
\end{DDbox}

Le paramètre de cette requête est un numéro de volume. On récupère tout d'abord le titre du volume associé, puis on effectue une jointure avec la table ALBUM pour récupérer le numéro de l'album dans sa collection et le numéro de la collection elle-même, puis on effectue une autre jointure avec la table COLLECTION pour finalement récupérer le nom de la collection.

\subsubsection{Liste des scénaristes pour une histoire}

\begin{DDbox}{\linewidth}
\begin{Verbatim}
select NUMERO_AUTEUR, NOM_AUTEUR from AUTEUR
	where NUMERO_AUTEUR in 
	(select NUMERO_AUTEUR from SCENARISE 
		where NUMERO_HISTOIRE = numero_histoire)
order by NOM_AUTEUR asc;
\end{Verbatim}
\end{DDbox}

Ici, le paramètre est un numéro d'histoire. On sélectionne les entrées de la table SCENARISE associées au numéro d'histoire, puis on récupère le nom des auteurs dont le numéro correspond aux entrées sélectionnées.

\subsubsection{Liste des dessinateurs pour une histoire}

\begin{DDbox}{\linewidth}
\begin{Verbatim}
select NUMERO_AUTEUR, NOM_AUTEUR from AUTEUR
	where NUMERO_AUTEUR in 
	(select NUMERO_AUTEUR from DESSINE 
		where NUMERO_HISTOIRE = numero_histoire)
order by NOM_AUTEUR asc;
\end{Verbatim}
\end{DDbox}

Idem avec la table DESSINE.

\subsubsection{Bibliographie d'un auteur}

\begin{DDbox}{\linewidth}
\begin{Verbatim}
select NOM_SERIE, NOM_HISTOIRE, ANNEE_PARUTION
from SERIE right outer join 
	(select * from HISTOIRE where NUMERO_HISTOIRE in 
		(select NUMERO_HISTOIRE from 
                    ((select * from DESSINE) union 
                     (select * from SCENARISE)) as DS 
		       where DS.NUMERO_AUTEUR = 2))as H
on SERIE.NUMERO_SERIE = H.NUMERO_SERIE
order by ANNEE_PARUTION asc;
\end{Verbatim}
\end{DDbox}

Ici, on fusionne les tables DESSINE et SCENARISE, ensuite on sélectionne les NUMERO\_HISTOIRE de l'auteur en question. On récupère les informations de ces NUMERO\_HISTROIRE dans la table HISTOIRE et on y ajoute avec une jointure droite les informations de la table SERIE. La jointure droite permet de récupérer les informations de la table SERIE mais aussi de conserver les histoires qui n'appartiennent pas à une serie.

\subsubsection{Historique de publication d'une histoire}

\begin{DDbox}{\linewidth}
\begin{Verbatim}
select ANNEE_EDITION, TITRE_VOLUME, NOM_EDITEUR
from EDITEUR inner join 
(select * from VOLUME 
	where NUMERO_VOLUME in 
	(SELECT NUMERO_VOLUME from APPARAITRE
		where NUMERO_HISTOIRE in 
		(SELECT NUMERO_HISTOIRE from HISTOIRE
			where NOM_HISTOIRE = 'nom_histoire'))) as V
on EDITEUR.NUMERO_EDITEUR = V.NUMERO_EDITEUR
order by ANNEE_EDITION asc;
\end{Verbatim}
\end{DDbox}

On selectionne successivement le NUMERO\_HISTOIRE à partir de NOM\_HISTOIRE, les NUMERO\_VOLUME à partir du NUMERO\_HISTOIRE, et finalement les informations des volumes à partir des NUMERO\_VOLUME. Ensuite, une jointure naturelle est faite avec la table EDITEUR pour sélectionner finalement ANNEE\_EDITION, TITRE\_VOLUME et NOM\_EDITEUR.


\subsubsection{Histoires différentes ayant le même titre}

\begin{DDbox}{\linewidth}
\begin{Verbatim}
select * from HISTOIRE 
where NOM_HISTOIRE in
(select NOM_HISTOIRE
 from HISTOIRE
 group by NOM_HISTOIRE
 having count(NUMERO_HISTOIRE) >= 2)
order by NOM_HISTOIRE asc;
\end{Verbatim}
\end{DDbox}

Tout d'abord, on cherche les NOM\_HISTOIRE qui ne sont pas uniques. Pour cela, on groupe par NOM\_HISTOIRE et on garde seulement les NOM\_HISTOIRE qui apparaissent au moins deux fois. Finalement on récupère les informations des histoires portant ces noms.


\subsubsection{Liste des auteurs collaborant à une revue durant une certaine periode}

\begin{DDbox}{\linewidth}
\begin{Verbatim}
select A.NUMERO_AUTEUR, A.NOM_AUTEUR, count(*) as NB_NUMEROS from AUTEUR as A
inner join
(((select * from DESSINE) union distinct 
   (select * from SCENARISE)) as R1
    inner join 
      (select NUMERO_VOLUME,NUMERO_HISTOIRE 
      from APPARAITRE
      where NUMERO_VOLUME in 
        (select NUMERO_VOLUME from VOLUME
        where NUMERO_VOLUME in (select NUMERO_VOLUME from REVUE)
        and TITRE_VOLUME='nom_revue'
        and ANNEE_EDITION>'annee_min'
        and ANNEE_EDITION<'annee_max')) as R2
    on R1.NUMERO_HISTOIRE = R2.NUMERO_HISTOIRE)
on A.NUMERO_AUTEUR = R1.NUMERO_AUTEUR
group by A.NOM_AUTEUR
order by NOM_AUTEUR asc;
\end{Verbatim}
\end{DDbox}

La réalisation de cette requête peut être réalisée en quatre étapes:
\begin{itemize}
\item Récupérer la liste des NUMERO\_VOLUME de la revue en question et qui ont été publiés entre les deux dates.
\item Récupérer les paires NUMERO\_VOLUME, NUMERO\_HISTOIRE correspondant aux NUMERO\_VOLUME précédemment trouvés.
\item Faire une jointure naturelle des paires précédentes avec l'union sans doublons des tables DESSINE et SCENARISE.
\item Grouper par NOM\_AUTEUR et compter le nombre de numéros auquels l'auteur à participé.
\end{itemize}

\subsection{Statistiques}
\subsubsection{Nombre d'histoires auxquelles un auteur a participé}

\begin{DDbox}{\linewidth}
\begin{Verbatim}
select count(NUMERO_HISTOIRE) as NB_HISTOIRES from  
((select NUMERO_HISTOIRE, NUMERO_AUTEUR from DESSINE where NUMERO_AUTEUR=1) 
union distinct 
(select NUMERO_HISTOIRE, NUMERO_AUTEUR from SCENARISE where NUMERO_AUTEUR=1)) as R1;
\end{Verbatim}
\end{DDbox}

On fait l'union sans doublons des lignes des tables DESSINE et SCENARISE dont le NUMERO\_AUTEUR est celui désiré. Et on compte le nombre de lignes.

\subsubsection{La série ayant le plus grand nombre d'auteurs}

\begin{DDbox}{\linewidth}
\begin{Verbatim}
select NUMERO_SERIE, max(NB_AUTEURS) as MAX_NB_AUTEURS from (
select NUMERO_SERIE, count(*) as NB_AUTEURS
from (HISTOIRE inner join ((select * from SCENARISE)
                          union distinct (select * from DESSINE))as R1 
	       on HISTOIRE.NUMERO_HISTOIRE = R1.NUMERO_HISTOIRE)
where NUMERO_SERIE is not NULL
group by NUMERO_SERIE) as R2;
\end{Verbatim}
\end{DDbox}

On fait l'union des tables DESSINE et SCENARISE puis une jointure avec la table HISTOIRE. On groupe par NUMERO\_SERIE en enlevant les histoires qui n'ont pas de serie. On compte pour chaque serie le nombre d'hisoires. Et on selectionne le maximum.

\subsubsection{Les histoires classées selont le nombre de fois où elles apparaissent en album}

\begin{DDbox}{\linewidth}
\begin{Verbatim}
select R1.NUMERO_HISTOIRE, NOM_HISTOIRE, count(*) as NB_APPARITIONS from
(HISTOIRE inner join (select * from APPARAITRE
                      where NUMERO_VOLUME in
                       (select NUMERO_VOLUME from ALBUM)) as R1
          on HISTOIRE.NUMERO_HISTOIRE=R1.NUMERO_HISTOIRE)
group by R1.NUMERO_HISTOIRE;
\end{Verbatim}
\end{DDbox}

On selectionne les NUMERO\_VOLUME des albums. On récupère les couples NUMERO\_VOLUME,NUMERO\_HISTOIRE. On fait la jointure naturelle avec la table HISTOIRE. On groupe par NUMERO\_HISTORE et on compte le nombre de fois où elles apparaissent.

\subsubsection{Le nombre moyen d'auteurs participant à une revue pendant une période donnée}

\begin{DDbox}{\linewidth}
\begin{Verbatim}
select avg(NB_AUTEURS) as NB_AUTEURS_MOY from(
select count(*) as NB_AUTEURS,R4.NUMERO_VOLUME from
(((select * from SCENARISE) union distinct (select * from DESSINE)) as R3
inner join
((select NUMERO_HISTOIRE, NUMERO_VOLUME from APPARAITRE) as R4
inner join
(select NUMERO_VOLUME from VOLUME
			where NUMERO_VOLUME in (select NUMERO_VOLUME from REVUE)
			and TITRE_VOLUME='PICSOU MAGAZINE'
			and ANNEE_EDITION>'2000'
			and ANNEE_EDITION<'2020') as R2
on R2.NUMERO_VOLUME=R4.NUMERO_VOLUME) 
on R4.NUMERO_HISTOIRE=R3.NUMERO_HISTOIRE)
group by R4.NUMERO_VOLUME) as R1;
\end{Verbatim}
\end{DDbox}

On récupère la liste des NUMERO\_VOLUME de la revue en question et qui ont été publiés entre les deux dates. Ensuite on fait la jointure naturelle avec la table APPARAITRE puis avec l'union de DESSINE et SCENARISE. On groupe par NUMERO\_VOLUME et on compte le nombre de participants pour chaque revue. Finalement, on fait la moyenne du nombre de participants pour chaque revue.

\section{Utilisation}

\subsection{Environnement d'exécution}

L'interface se lance à travers un navigateur web et nécessite donc l'installation d'un serveur HTTP. Des fonctionnalités nécessitant la version 5 du langage PHP ont été utilisées. Le fichier d'index est le fichier \textit{index.php} se trouvant dans le répertoire \textit{php} de notre projet.

\subsection{Notice d'utilisation}

Au lancement, il est proposé à l'utilisateur d'entrer les informations nécessaires pour la connexion à la base de données : adresse du serveur MySQL (par défaut \textit{localhost}), nom de la base, nom d'utilisateur et mot de passe.

Sur la page principale, différents liens sont proposés. Les premiers correspondent respectivement à la création, au remplissage et à la suppression des tables \footnote{Note : Après suppression des tables, il est nécessaire de relancer ``Initialiser la base'' avant de la remplir à nouveau.}. Les liens suivants correspondent à chacune des tables. En cliquant dessus, l'utilisateur pourra visualiser le contenu de cette table et il sera proposé d'ajouter, de supprimer ou de mettre à jour une entrée de cette table.

~

\noindent Pour chacune de ces actions, l'utilisateur peut choisir les paramètres de l'action à l'aide d'un menu déroulant, ou rentrer à la main chacun des paramètres lorsque la valeur n'est pas déterminée (comme le nom d'un auteur par exemple). L'utilisateur valide son action en cliquant sur le bouton \textit{Envoyer} au bas de l'action qu'il a choisi d'effectuer. Le résultat de la requête apparait ensuite au bas de la page.

~

\noindent Les derniers liens correpondent aux requêtes de consultation et de statistiques demandées dans le sujet. Les paramètres sont choisis de la même manière que précédemment.\\

Attention : L'activation des triggers n'est possible qu'en executant le fichier \textit{base\_mysql.sql} dans mysql et non depuis l'interface. 

\section{Conclusion}
Une solution alternative au trigger aurait été de créer une collection vide pour chaque éditeur dans laquelle on aurait rassemblé tous les albums sans collection de cet éditeur. Ainsi on aurait plus de relation entre un volume et un éditeur mais une relation entre éditeur et collection et une autre entre éditeur et revue. Cette solution nous a paru moins claire et moins cohérente au niveau de l'organisation des tables.\\
Le modèle choisi et l'utilisation de trigger nous a permis d'avoir une organisation des différentes informations simple et claire tout en assurant les différentes contraintes du sujets.

\end{document}
